﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CrumbleCore.xaml.cs" company="None">
//   <Author>Adrian von Allmen, 2013</Author>
// </copyright>
// <summary>
//   Conmtain the Core UserControl. Currently not MVVM Approach
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Crumble
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using System.Data;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Resources;
    using System.Text;
    using System.Windows.Forms;
    using System.Windows.Threading;

    using CrumbleLibrary;

    using Microsoft.ComponentModel.Composition.Diagnostics;
    using Microsoft.Reporting.WinForms;

    using MessageBox = System.Windows.MessageBox;
    using System.Windows.Media;
    using System.Data.SqlClient;

    /// <summary>
    /// Core UserControl
    /// </summary>
    public partial class CrumbleCore
    {
        #region Fields
        
        /// <summary>
        /// showReport Variable for Threading
        /// </summary>
        public ShowReport ShowReportThread;

        /// <summary>
        /// A list of Errors
        /// </summary>
        private readonly Dictionary<DateTime, string> errorList = new Dictionary<DateTime, string>();

        /// <summary>
        /// Class for create an object oriented Filter Summary (WPF Binding)
        /// </summary>
        private readonly ObservableCollection<FilterDisplay> filterTabs = new ObservableCollection<FilterDisplay>();

        /// <summary>
        /// Is the application in initial state
        /// </summary>
        private bool firstRun = true;

        /// <summary>
        /// The DB Value for ConnectionString Retrieval
        /// </summary>
        private string databaseProfile = string.Empty;

        /// <summary>
        /// The report unique id.
        /// </summary>
        private string reportUniqueId = string.Empty;

        /// <summary>
        /// The default language as ResourceManager File.
        /// </summary>
        private ResourceManager defaultLanguageResource;

        /// <summary>
        /// The report language as ResourceManager File.
        /// </summary>
        private ResourceManager reportLangaugeResource;

        /// <summary>
        /// The CultureInfo of a specific language.
        /// </summary>
        private CultureInfo languageCulture = CultureInfo.GetCultureInfo("de");

        /// <summary>
        /// ReportDataSource of the running Report.
        /// </summary>
        private ReportDataSource reportData;

        /// <summary>
        /// The Active Report File
        /// </summary>
        private IReport activeReport;

        /// <summary>
        /// List of the custom ParameterItem Class
        /// </summary>
        private Dictionary<string, FilterParameterItem> reportParameterList;
        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="CrumbleCore"/> class.
        /// </summary>
        public CrumbleCore()
        {
            this.InitializeComponent();
            this.FilterTabControl.ItemsSource = this.filterTabs;
            this.CompositionHandling();
        }
        
        #region Delegates

        /// <summary>
        /// Delegated ShowReport Routine
        /// </summary>
        public delegate void ShowReport();

        /// <summary>
        /// Set Filter Call
        /// </summary>
        private delegate void SetFilter();

        /// <summary>
        /// The set filter restriction.
        /// </summary>
        private delegate void SetFilterRestriction();

        /// <summary>
        /// The add dynamic filter.
        /// </summary>
        private delegate void AddDynamicFilter();

        /// <summary>
        /// The delegate set filter visible state.
        /// </summary>
        private delegate void DelegateSetFilterVisibleState();

        /// <summary>
        /// The delegate set report viewer visible state.
        /// </summary>
        // ReSharper disable UnusedMember.Local
        private delegate void DelegateSetReportViewerVisibleState();

        // ReSharper restore UnusedMember.Local
        #endregion

        #region Properities
        
        /// <summary>
        /// Gets or sets all MEF compatible Reports.
        /// </summary>
        [ImportMany("Reports", typeof(IReport))]
        private IEnumerable<Lazy<IReport, IReportMetadataAttribute>> AllReports { get; set; }
        
        /// <summary>
        /// Gets or sets all MEF compatible Filter.
        /// </summary>
        [ImportMany("ReportFilter", typeof(IFilterPlugins))]
        private IEnumerable<Lazy<IFilterPlugins, IFilterPluginMetadataAttribute>> Filters { get; set; }

        #endregion

        #region Interface Features
        
        /// <summary>
        /// Get a list of Reports and GUID.
        /// </summary>
        /// <returns>
        /// The <see><cref>Dictionary</cref></see> with UniqueID and Report Name.
        /// </returns>
        public Dictionary<string, string> GetReportList()
        {
            return this.AllReports.ToDictionary(item => item.Metadata.Id, item => item.Metadata.Reportname);
        }

        /// <summary>
        /// Set Visibility of Control and Content
        /// </summary>
        public void Show()
        {
            this.SetFilterVisibleState();
            this.SetReportViewerVisibleState();
        }

        /// <summary>
        /// The add filter.
        /// </summary>
        /// <param name="reportId"> The report id. </param>
        /// <param name="filterValue"> The filter value. </param>
        /// <returns>State if successful </returns>
        public bool AddFilter(string reportId, string filterValue)
        {
            var filterItem = new FilterParameterItem(HelperLibrary.FilterParameterType.String, "Filter", filterValue);
            this.reportParameterList.Add("Filter", filterItem);
            return true;
        }
        #endregion

        #region DLL Features

        /// <summary>
        /// The reset report.
        /// </summary>
        public void ResetReport()
        {
            this.activeReport = null;
            this.reportParameterList = null;
        }
        #endregion

        #region Report Methods
        /// <summary>
        /// The set report viewer visible state.
        /// </summary>
        private void SetReportViewerVisibleState()
        {
            // ToDo: Make ReportControl Modular (for inject any reporting technology)
            if (!this.ReportViewerControl.InvokeRequired)
            {
                this.ReportViewerControl.Visible = true;
                this.ReportViewerControl.RefreshReport();
                this.ReportViewerControl.SetDisplayMode(Microsoft.Reporting.WinForms.DisplayMode.Normal);
            }
            else
            {
                this.ReportViewerControl.Invoke(
                    new MethodInvoker(this.SetReportViewerVisibleState));
            }
        }
        
        /// <summary>
        /// Set all passed and stored parameter to the report.
        /// </summary>
        private void SetPassedReportParameter()
        {
            var reportParamList = new List<ReportParameter>();
            if (this.reportParameterList != null)
            {
                foreach (var item in this.reportParameterList)
                {
                    if (item.Value.Type != HelperLibrary.FilterParameterType.Report)
                    {
                        continue;
                    }

                    var reportParam = new ReportParameter { Name = item.Value.Name };
                    reportParam.Values.Add(Convert.ToString(item.Value.Value));

                    reportParamList.Add(reportParam);
                }

                var reportParamVersion = new ReportParameter("strReportVersion", this.activeReport.GetVersion);
                reportParamList.Add(reportParamVersion);

                this.ReportViewerControl.LocalReport.SetParameters(reportParamList);
            }
        }
        #endregion

        #region Report Loading Features
        
        public void Load(string reportID, ShowReport showReport)
        {
            if (!reportID.Equals(string.Empty))
            {
                this.reportUniqueId = reportID;
                bool isLoaded = this.Load(reportID);

                if (isLoaded)
                {
                    showReport();
                }
            }
        }

        public bool Load(string reportId)
        {
            bool isLoaded = false;

            if (!reportId.Equals(string.Empty))
            {
                this.reportUniqueId = reportId;
                isLoaded = this.Load();
            }

            return isLoaded;
        }

        private bool Load()
        {
            var reports = from lazyPlugin in this.AllReports
                          let metadata = lazyPlugin.Metadata
                          where metadata.Id == this.reportUniqueId
                          select lazyPlugin.Value;

            var count = reports.Count();
            if (count == 1)
            {
                foreach (var report in reports)
                {
                    var reportDefinition = report.LoadReport();
                    if (reportDefinition == null)
                    {
                        continue;
                    }

                    if (this.ReportViewerControl.InvokeRequired)
                    {
                        var report1 = report;
                        this.ReportViewerControl.Invoke(new MethodInvoker(() => this.DelegateLoad(report1, reportDefinition)));
                    }
                    else
                    {
                        this.DelegateLoad(report, reportDefinition);
                    }

                    if (this.reportParameterList == null || this.reportParameterList.Count == 0)
                    {
                        this.reportParameterList = report.ParameterItems;
                    }

                    this.SetFilters(report);
                    this.activeReport = report;
                    this.SetFilterVisibility();
                    this.AddDynamicFilters();

                    this.ReportDataManagement(report);

                    var processigCheck = this.ReportViewerControl.LocalReport.IsReadyForRendering;
#if DEBUG
                    processigCheck = true;
#endif
                    
                    if (processigCheck)
                    {
                        if (this.firstRun == false)
                        {
                            return true;
                        }

                        if (report.LoadOnStartup == false)
                        {
                            try
                            {
                                Assembly assembly = Assembly.GetExecutingAssembly();
                                // ToDo: Initial Report View, for example to explain the report (whats necessary to run, etc)
                                /*var initialReportDefinition = ??
                                this.ReportViewerControl.LocalReport.LoadReportDefinition(
                                    LocalizationHelper.TranslateReport(
                                        report.GetReportType,
                                        initialReportDefinition,
                                        this.languageCulture,
                                        this.defaultLanguageResource));
                                */
                                this.firstRun = false;
                                return true;
                            }
                            catch
                            {
                                this.firstRun = false;
                            }
                        }
                        else
                        {
                            this.firstRun = false;
                            return true;
                        }

                        this.firstRun = false;
                        return false;
                    }
                }
            }
            else
            {
                if (count == 0)
                {
                    throw new Exception("No Report found");
                }

                throw new Exception(string.Format("Duplication Error: Total {0} Report found. Cannot load specific", count));
            }

            return false;
        }

        private void ReportDataManagement(IReport report)
        {
            if (report == null)
            {
                return;
            }

            this.ReportViewerControl.LocalReport.DataSources.Clear();
            this.reportParameterList = this.activeReport.ConvertValues(this.reportParameterList);

            this.activeReport.ParameterItems = this.reportParameterList;
            if (report.GetReportType == HelperLibrary.ReportType.MSRS)
            {
                var valueCheck = report.GetDataInstruction() as ReportDataSource;
                if (valueCheck != null)
                {
                    this.reportData = valueCheck;

                    if (this.reportData != null)
                    {
                        if (this.firstRun == false)
                        {
                            if (this.ReportViewerControl.InvokeRequired)
                            {
                                this.ReportViewerControl.Invoke(
                                    new MethodInvoker(() => this.DelegateLoad(report, report.LoadReport())));
                            }
                            else
                            {
                                this.DelegateLoad(report, report.LoadReport());
                            }
                        }

                        this.ReportViewerControl.LocalReport.DataSources.Clear();
                        this.ReportViewerControl.LocalReport.DataSources.Add(this.reportData);                      

                        // ToDo: Header Handling for Corporate Identity / Company Requirements
                        var emptyTable = new DataSet().Tables.Add("ReportHeader");
                        this.ReportViewerControl.LocalReport.DataSources.Add(new ReportDataSource("ReportHeader", emptyTable));

                        this.SetPassedReportParameter();
                    }
                }
            }
        }

        #endregion

        #region Filter Methods

        /// <summary>
        /// Set the Visibility of Filter and reset index within TabControl.
        /// </summary>
        private void SetFilterVisibleState()
        {
            if (this.FilterTabControl.Dispatcher.CheckAccess())
            {
                // Todo: Depend on UI decision: Dockable Filtercontrol Visibility based on contains Filter and Set Tabcontrol to Index 0
                if (!(this.filterTabs.Count > 0))
                {
                    
                    // Todo: Docking .IsCloseable = true;
                    // Todo: Docking .Close();
                }
                else
                {
                    // Todo: Docking .IsCloseable = false;
                    // Todo: Docking .FilterDocking.Show();
                    this.FilterTabControl.Visibility = System.Windows.Visibility.Visible;
                    this.FilterTabControl.SelectedIndex = 0;
                }
            }
            else
            {
                this.FilterTabControl.Dispatcher.Invoke(
                    DispatcherPriority.Normal, new DelegateSetFilterVisibleState(this.SetFilterVisibleState));
            }
        }

        /// <summary>
        /// Set Filters
        /// </summary>
        /// <param name="report">
        /// The report.
        /// </param>
        private void SetFilters(IReport report)
        {
            if (this.FilterTabControl.Dispatcher.CheckAccess())
            {
                this.filterTabs.Clear();
                foreach (var filertId in report.SetFilter())
                {
                    KeyValuePair<string, List<FilterParameterItem>> id = filertId;
                    var activeFilter = from lazyPlugin in this.Filters
                                       let metadata = lazyPlugin.Metadata
                                       where metadata.Id == id.Key
                                       select lazyPlugin.Value;

                    foreach (var filter in activeFilter)
                    {
                        filter.DynamicParameterList.Clear();
                        this.filterTabs.Add(
                            new FilterDisplay() { FilterUserControl = filter, DisplayName = filter.GetDisplayName });
                        foreach (var paramItem in filertId.Value)
                        {
                            if (paramItem.Type == HelperLibrary.FilterParameterType.FilterControl)
                            {
                                filter.SetDefaultValue(paramItem.Name, paramItem.Value);
                            }
                        }
                    }
                }
            }
            else
            {
                this.FilterTabControl.Dispatcher.Invoke(
                    DispatcherPriority.Normal, new SetFilter(() => this.SetFilters(report)));
            }
        }
        
        /// <summary>
        /// Set which filter details has to be collapsed.
        /// </summary>
        private void SetFilterVisibility()
        {
            if (this.FilterTabControl.Dispatcher.CheckAccess())
            {
                foreach (var myTab in this.filterTabs)
                {
                    foreach (var myFilterParam in this.GetFilterVisibility())
                    {
                        myTab.FilterUserControl.SetFilterVisibility(myFilterParam.Key, myFilterParam.Value);
                    }
                }
                this.SetFilterVisibleState();
            }
            else
            {
                this.FilterTabControl.Dispatcher.Invoke(
                    System.Windows.Threading.DispatcherPriority.Normal,
                    new SetFilterRestriction(this.SetFilterVisibility));
            }
        }
        
        /// <summary>
        /// Add dynamical Filter-Controls to the MISC Filter Control
        /// </summary>
        private void AddDynamicFilters()
        {
            if (this.FilterTabControl.Dispatcher.CheckAccess())
            {
                foreach (var myTab in this.filterTabs)
                {
                    foreach (var myFilterParam in this.GetDynamicFilters())
                    {
                        myTab.FilterUserControl.AttachDynamicFilterItem(myFilterParam.Key, myFilterParam.Value);
                    }
                }

                this.SetFilterVisibleState();
            }
            else
            {
                this.FilterTabControl.Dispatcher.Invoke(
                    System.Windows.Threading.DispatcherPriority.Normal,
                    new AddDynamicFilter(this.AddDynamicFilters));
            }
        }
        
        /// <summary>
        /// Get a dictionary with all filter-details which should be hidden
        /// </summary>
        /// <returns> The dictionary which contains information for hiding filter. </returns>
        private Dictionary<string, bool> GetFilterVisibility()
        {
            var filterRestrictions = new Dictionary<string, bool>();
            if (this.reportParameterList != null)
            {
                foreach (var item in this.reportParameterList.Where(item => item.Value.Type == HelperLibrary.FilterParameterType.FilterVisibility))
                {
                    string restrictionRule = item.Value.Value.ToString();
                    if (filterRestrictions.ContainsKey(item.Value.Name))
                    {
                        filterRestrictions.Remove(item.Value.Name);
                    }

                    filterRestrictions.Add(item.Value.Name, Convert.ToBoolean(restrictionRule));
                }
            }

            return filterRestrictions;
        }

        /// <summary>
        /// Retrieve all dynamical configured filter controls
        /// </summary>
        /// <returns> The Dictionary with all dynamic defined controls. </returns>
        private Dictionary<string, FilterItemDynamic> GetDynamicFilters()
        {
            var dynamicFilter = new Dictionary<string, FilterItemDynamic>();
            if (this.reportParameterList != null)
            {
                foreach (var item in this.reportParameterList.Where(item => item.Value.Type == HelperLibrary.FilterParameterType.FilterDynamic))
                {
                    if (dynamicFilter.ContainsKey(item.Value.Name))
                    {
                        dynamicFilter.Remove(item.Value.Name);
                    }

                    var dynamicItem = item.Value.Value as FilterItemDynamic;
                    if (dynamicItem != null)
                    {
                        dynamicFilter.Add(item.Value.Name, dynamicItem);
                    }
                }
            }

            return dynamicFilter;
        }
        #endregion


        #region Internal Methods

        /// <summary>
        /// Load ReportDefinition as Delegate_Event
        /// </summary>
        /// <param name="report"> Report which used for load </param>
        /// <param name="reportDefinition"> The Report RDLC Definition as stream for injection</param>
        private void DelegateLoad(IReport report, object reportDefinition)
        {
            this.reportLangaugeResource = report.GetInternalTranslaion();
            this.ReportViewerControl.Reset();
            this.ReportViewerControl.LocalReport.DataSources.Clear();
            this.ReportViewerControl.ProcessingMode = ProcessingMode.Local;

            var listTranslations = new List<ResourceManager>
                                   {
                                       this.reportLangaugeResource,
                                       this.defaultLanguageResource
                                   };

            report.ReportLanguage = this.languageCulture;
            
            this.ReportViewerControl.LocalReport.LoadReportDefinition(LocalizationHelper.TranslateReport(report.GetReportType, reportDefinition, this.languageCulture, listTranslations));
        }
        #region CORE: Load and Threading

        #region Translation

        private void LoadTranslation()
        {
            var getFile = HelperLibrary.GetDefaultTranslationFile();
            if (getFile != null)
            {
                this.defaultLanguageResource = getFile;
            }
        }

        #endregion

        #endregion

        #endregion

        #region ToDo

        private void Click_ProcessReport(object sender, System.Windows.RoutedEventArgs e)
        {
            this.reportParameterList.Clear();
            foreach (var item in this.filterTabs)
            {
                foreach (var valueParam in item.GetList())
                {
                    var tempValue = new FilterParameterItem();
                    if (valueParam.Key.Substring(0, 1).Equals("@"))
                    {
                        tempValue.Type = HelperLibrary.FilterParameterType.MSSQL;
                        tempValue.Name = valueParam.Key;
                        var paramValueObject = valueParam.Value as SqlParameter;
                        if (paramValueObject != null)
                        {
                            tempValue.Value = valueParam.Value;
                        }
                        else
                        {
                            tempValue.Value = new SqlParameter(
                                valueParam.Key, HelperLibrary.GetMsSqlDbType(valueParam.Value.GetType()))
                            {
                                Value = valueParam.Value
                            };
                        }
                    }
                    else
                    {
                        var reportValidation = valueParam.Value as ReportParameter;
                        if (reportValidation != null)
                        {
                            tempValue = new FilterParameterItem(HelperLibrary.FilterParameterType.Report, valueParam.Key, reportValidation.Values[0]);
                        }
                        else
                        {
                            var dynamicParam = valueParam.Value as FilterItemDynamic;
                            tempValue = dynamicParam != null ? new FilterParameterItem(HelperLibrary.FilterParameterType.FilterDynamic, valueParam.Key, valueParam.Value) : new FilterParameterItem(HelperLibrary.FilterParameterType.MSSQL, valueParam.Key, valueParam.Value);
                        }
                    }

                    this.reportParameterList.Add(valueParam.Key, tempValue);
                }
            }

            this.ReportDataManagement(this.activeReport);
            if (this.ReportViewerControl.LocalReport.IsReadyForRendering)
            {
                //ToDo: Refresh Report Views based on type
                this.ReportViewerControl.LocalReport.Refresh();
                this.ReportViewerControl.RefreshReport();
            }

        }

        /// <summary>
        /// ToDo: Workaround for MEF Composition until HelperClass works
        /// </summary>
        private void CompositionHandling()
        {
            var catalogPath = new StringBuilder();

            try
            {
                // Read Composition for Report, Filter and future plugins
                var reportCatalog = new AggregateCatalog();
                string codeBase = Assembly.GetExecutingAssembly().CodeBase;
                var uri = new UriBuilder(codeBase);
                string path = Uri.UnescapeDataString(uri.Path);
                string assemblyDirectory = Path.GetDirectoryName(path);
                if (assemblyDirectory != null)
                {
                    catalogPath.AppendLine(assemblyDirectory);
                    reportCatalog.Catalogs.Add(new DirectoryCatalog(assemblyDirectory));
                }

#if DEBUG
                if (Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).Contains("Standalone"))
                {
                    var dirInfo = new DirectoryInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
                    string debugPath = dirInfo.Parent.Parent.Parent.Parent.FullName;
                   catalogPath.AppendLine(debugPath + @"\Reports\Report_Sample\bin\Debug");
                    catalogPath.AppendLine(debugPath + @"\Filters\Filter_Misc\bin\Debug");
                    reportCatalog.Catalogs.Add(new DirectoryCatalog(debugPath + @"\Reports\Report_Sample\bin\Debug"));
                    reportCatalog.Catalogs.Add(new DirectoryCatalog(debugPath + @"\Filters\Filter_Misc\bin\Debug"));
                }
#endif
                var reportWorker = new CompositionContainer(reportCatalog);
                reportWorker.ComposeParts(this);

                var ci = new CompositionInfo(reportCatalog, reportWorker);
                CompositionInfoTextFormatter.Write(ci, Console.Out);
            }
            catch (ReflectionTypeLoadException typeLException)
            {
                var loaderMessages = new StringBuilder();
                loaderMessages.AppendLine(
                    "While trying to load composable parts the follwing loader exceptions were found: ");
                foreach (var loaderException in typeLException.LoaderExceptions)
                {
                    loaderMessages.AppendLine(loaderException.Message);
                }

                loaderMessages.AppendLine("Directory Involved");
                loaderMessages.AppendLine(catalogPath.ToString());

                throw new Exception(loaderMessages.ToString(), typeLException);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        #endregion

    }
}
